{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([[7.07106781, 7.06400028, 7.05693985, ..., 7.04988652, 7.05693985,\n",
       "        7.06400028],\n",
       "       [7.06400028, 7.05692568, 7.04985815, ..., 7.04279774, 7.04985815,\n",
       "        7.05692568],\n",
       "       [7.05693985, 7.04985815, 7.04278354, ..., 7.03571603, 7.04278354,\n",
       "        7.04985815],\n",
       "       ...,\n",
       "       [7.04988652, 7.04279774, 7.03571603, ..., 7.0286414 , 7.03571603,\n",
       "        7.04279774],\n",
       "       [7.05693985, 7.04985815, 7.04278354, ..., 7.03571603, 7.04278354,\n",
       "        7.04985815],\n",
       "       [7.06400028, 7.05692568, 7.04985815, ..., 7.04279774, 7.04985815,\n",
       "        7.05692568]])"
      ]
     },
     "execution_count": 2,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# 利用数组进行数据处理\n",
    "import numpy as np\n",
    "\n",
    "# ndarray 数组可以将许多数据处理任务表述为简洁的数组表达式\n",
    "# 用数组表达式 代替 循环 ，通常称之为矢量化\n",
    "# 矢量化数组运算 比 等价的纯python方法快\n",
    "# 特别是 广播 功能，针对矢量化计算的强大技巧\n",
    "\n",
    "# 计算函数 z(x,y) = sqrt(x ** 2 + y ** 2)\n",
    "# 随机生成数据 1000 数据点\n",
    "points = np.arange(-5, 5, 0.01)\n",
    "# 赋值给 X Y\n",
    "X_points, Y_points = np.meshgrid(points, points)\n",
    "Z = np.sqrt(X_points ** 2 + Y_points ** 2)\n",
    "Z"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "Text(0.5, 1.0, 'Image plot of $\\\\sqrt{x^2 + y^2}$ for g grid of values')"
      ]
     },
     "execution_count": 7,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "image/png": "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\n",
      "text/plain": [
       "<Figure size 432x288 with 2 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "import matplotlib.pyplot as plt\n",
    "\n",
    "plt.imshow(Z, cmap=plt.cm.gray)\n",
    "plt.colorbar()\n",
    "plt.title(\"Image plot of $\\sqrt{x^2 + y^2}$ for g grid of values\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[1.1, 2.2, 1.3, 1.4, 2.5]"
      ]
     },
     "execution_count": 9,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# 将条件逻辑表述为数组运算\n",
    "# numpy.where 函数是三元表达式 x if condition else y 的矢量化版本\n",
    "\n",
    "# 两个 值数组\n",
    "x_arr = np.array([1.1, 1.2, 1.3, 1.4, 1.5])\n",
    "y_arr = np.array([2.1, 2.2, 2.3, 2.4, 2.5])\n",
    "# logic 布尔数组\n",
    "condition = np.array([True, False, True, True, False])\n",
    "\n",
    "# 列表推导式\n",
    "result = [(x if c else y) for x, y, c in zip(x_arr, y_arr, condition)]\n",
    "result"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([1.1, 2.2, 1.3, 1.4, 2.5])"
      ]
     },
     "execution_count": 10,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# question 1；处理大数组 效率不高\n",
    "# question 2：无法应用于多维数组\n",
    "# solution：矢量化 np.where\n",
    "result_vector = np.where(condition, x_arr, y_arr)\n",
    "result_vector"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([[-0.36545542, -1.69028732,  1.50362672, -0.16230993],\n",
       "       [ 0.14755199,  1.70812449, -0.01956727, -0.38584389],\n",
       "       [-0.32432926, -1.22588167, -1.01856873,  1.1215925 ],\n",
       "       [-0.30130488, -1.85504669,  0.52817802, -0.44144115]])"
      ]
     },
     "execution_count": 11,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# 随机生成矩阵数据，正值 替换为 2，负值 替换为 -2\n",
    "arr_random = np.random.randn(4, 4)\n",
    "arr_random"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([[False, False,  True, False],\n",
       "       [ True,  True, False, False],\n",
       "       [False, False, False,  True],\n",
       "       [False, False,  True, False]])"
      ]
     },
     "execution_count": 12,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "arr_random > 0"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([[-2, -2,  2, -2],\n",
       "       [ 2,  2, -2, -2],\n",
       "       [-2, -2, -2,  2],\n",
       "       [-2, -2,  2, -2]])"
      ]
     },
     "execution_count": 14,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "np.where(arr_random > 0, 2, -2)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([[ 1.54596748, -0.16885519, -0.36121907,  1.2951352 ],\n",
       "       [-0.75033682,  0.8693843 , -0.81423982, -0.42273806],\n",
       "       [ 0.73066602, -0.61598718, -0.0573866 , -0.58170444],\n",
       "       [-0.92959475, -1.00880221, -0.7419588 , -1.09902946],\n",
       "       [ 2.34924397, -0.20807729, -0.06805245, -1.56781565]])"
      ]
     },
     "execution_count": 15,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# 通过数组上的一组数学函数对整个数组或某个轴向的数据进行统计计算\n",
    "# sum、 mean、std等聚合计算（aggregation， 通常叫做约简（reduction） ）\n",
    "# 既可以当做数组的实例方法调用， 也可以当做顶级NumPy函数使用\n",
    "\n",
    "# 生成了一些正态分布随机数据， 然后做了聚类统计\n",
    "arr_Gaussian = np.random.randn(5, 4)\n",
    "arr_Gaussian"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "the mean of dataset:  -0.13027004084141658\n",
      "the sum of dataset:  -2.6054008168283316\n",
      "the mean of dataset:  -0.13027004084141658\n"
     ]
    }
   ],
   "source": [
    "print(\"the mean of dataset: \", arr_Gaussian.mean())\n",
    "print(\"the sum of dataset: \", arr_Gaussian.sum())\n",
    "print(\"the mean of dataset: \", np.mean(arr_Gaussian))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "the mean of dataset for the axis1:  [ 0.5777571  -0.2794826  -0.13110305 -0.9448463   0.12632464]\n",
      "the sum of dataset for the axis0:  [ 2.94594591 -1.13233757 -2.04285674 -2.37615241]\n"
     ]
    }
   ],
   "source": [
    "# 这些 统计函数 可以接受 axis 选项参数，计算指定轴上的数据的统计值\n",
    "\n",
    "# 行 的平均值\n",
    "print(\"the mean of dataset for the axis1: \", arr_Gaussian.mean(axis = 1))\n",
    "# 列 的求和\n",
    "print(\"the sum of dataset for the axis0: \", arr_Gaussian.sum(axis = 0))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "51"
      ]
     },
     "execution_count": 18,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# 统计方法        说明\n",
    "# sum             对数组中全部或者某轴的元素求和，零长度数组sum为0\n",
    "# mean            算术平均数，零长度数组mean为 NaN\n",
    "# std\\var         分别为标准差 和 方差\n",
    "# min\\max         最大值和最小值\n",
    "# argmin\\argmax   分别为最大和最小元素索引\n",
    "# cumsum          所有元素的累计和\n",
    "# cumprod         所有元素的累计积\n",
    "\n",
    "# 这些统计方法中，布尔值 会被强制转化为 1（True）和 0（False）\n",
    "# sum 统计布尔型数组中的 True 个数\n",
    "arr = np.random.randn(100)\n",
    "(arr > 0).sum()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "True\n",
      "False\n"
     ]
    }
   ],
   "source": [
    "# any and all method 对于布尔型数组非常有用\n",
    "# any 测试数组中是否存在一个或者多个True\n",
    "# all 测试数组中所有值是否都是True\n",
    "bools = np.array([False, False, True, False])\n",
    "print(bools.any())\n",
    "print(bools.all())"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([ 1.25487442, -2.20225111,  0.49354525, -0.07813859,  0.91287791,\n",
       "       -0.41495535])"
      ]
     },
     "execution_count": 20,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# 排序\n",
    "# 与 Python 内置方法一致，numpy 数组一样可以 sort 方法就地排序\n",
    "arr = np.random.randn(6)\n",
    "arr"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 22,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([-2.20225111, -0.41495535, -0.07813859,  0.49354525,  0.91287791,\n",
       "        1.25487442])"
      ]
     },
     "execution_count": 22,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "arr.sort()\n",
    "arr"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 23,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([[ 1.15631365, -0.7308923 , -2.45565255],\n",
       "       [ 2.08875785, -0.17964062,  1.13258301],\n",
       "       [-0.41356165,  0.49831558,  0.05697042],\n",
       "       [-0.14260991,  1.22808541,  1.18712298],\n",
       "       [ 0.16233879, -0.77538383, -0.54969005]])"
      ]
     },
     "execution_count": 23,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# 多维数组可以在指定轴上进行排序，将轴编号传入\n",
    "arr = np.random.randn(5, 3)\n",
    "arr"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 24,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([[-2.45565255, -0.7308923 ,  1.15631365],\n",
       "       [-0.17964062,  1.13258301,  2.08875785],\n",
       "       [-0.41356165,  0.05697042,  0.49831558],\n",
       "       [-0.14260991,  1.18712298,  1.22808541],\n",
       "       [-0.77538383, -0.54969005,  0.16233879]])"
      ]
     },
     "execution_count": 24,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# 在 列 上进行排序\n",
    "arr.sort(1)\n",
    "arr"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 25,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "-1.6916849812362698"
      ]
     },
     "execution_count": 25,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# 顶级方法np.sort返回的是数组的已排序副本， 而就地排序则会修改数组本身。\n",
    "# 计算数组分位数最简单的办法是对其进行排序， 然后选取特定位置的值：\n",
    "large_arr = np.random.randn(1000)\n",
    "large_arr.sort()\n",
    "large_arr[int(0.05 * len(large_arr))] # 5% quantile"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 26,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array(['Bob', 'Joe', 'Will'], dtype='<U4')"
      ]
     },
     "execution_count": 26,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# 唯一化 其他集合逻辑\n",
    "# 针对一维 ndarray 数组的基本集合运算\n",
    "# np.unique,找出数组中唯一值并返回已排序的结果\n",
    "names = np.array(['Bob', 'Joe', 'Will', 'Bob', 'Will', 'Joe', 'Joe'])\n",
    "np.unique(names)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 27,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([1, 2, 3, 4])"
      ]
     },
     "execution_count": 27,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "ints = np.array([3, 3, 3, 2, 2, 1, 1, 4, 4])\n",
    "np.unique(ints)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 28,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 数组的集合运算\n",
    "# 方法            说明\n",
    "# unique(x)        计算x中唯一元素，并返回有序结果\n",
    "# intersect1d(x,y) 计算x和y的公共元素，并返回有序结果\n",
    "# union1d(x,y)     计算x和y的并集，并返回有序结果\n",
    "# in1d(x,y)        得到表示“x的元素是否包含于y”的布尔数组\n",
    "# setdiff1d(x,y)   集合的差，即元素在x中不在y中\n",
    "# setxor1d(x,y)    集合的对称差，即存在于一个数组中但不同时存在于两个数组中的元素\n",
    "\n",
    "\n",
    "# 用户数组的文件输入输出\n",
    "# NumPy能够读写磁盘上的文本数据或二进制数据\n",
    "# 只讨论NumPy的内置二进制格式，因更多的使用pandas加载文本或表格数据\n",
    "# np.save和np.load是读写磁盘数组数据的两个主要函数\n",
    "# 默认情况下， 数组是以未压缩的原始二进制格式保存在扩展名为.npy的文件中的\n",
    "\n",
    "# the path of dataset\n",
    "dataset_path = './../datasest/'\n",
    "\n",
    "arr = np.arange(10)\n",
    "np.save(dataset_path + 'some_array', arr)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 29,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])"
      ]
     },
     "execution_count": 29,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# 如果文件路径末尾没有扩展名.npy， 则该扩展名会被自动加上\n",
    "# 然后就可以通过np.load读取磁盘上的数组\n",
    "\n",
    "np.load(dataset_path + 'some_array.npy')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 30,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 通过np.savez可以将多个数组保存到一个未压缩文件中， \n",
    "#     将数组以关键字参数的形式传入即可：\n",
    "np.savez(dataset_path + 'array_archive.npz', a=arr, b=arr)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 31,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])"
      ]
     },
     "execution_count": 31,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# 加载.npz文件时， 你会得到一个类似字典的对象， 该对象会对各个数组进行延迟加载：\n",
    "arch = np.load(dataset_path + 'array_archive.npz')\n",
    "arch['b']"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 32,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 如果要将数据压缩， 可以使用numpy.savez_compressed：\n",
    "np.savez_compressed(dataset_path + 'arrays_compressed.npz', a=arr, b=arr)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 33,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[[ 28.  64.]\n",
      " [ 67. 181.]]\n",
      "============================\n",
      "[[ 28.  64.]\n",
      " [ 67. 181.]]\n"
     ]
    }
   ],
   "source": [
    "# 性线代数\n",
    "# 矩阵乘法、矩阵分解、行列式等等\n",
    "# numpy 提供矩阵乘法的 dot 运算函数\n",
    "# 即是一个数组方法，也是一个numpy空间的函数\n",
    "# x.dot(y) 等价于 np.dot(x,y)\n",
    "\n",
    "x = np.array([[1., 2., 3.], [4., 5., 6.]])\n",
    "y = np.array([[6., 23.], [-1, 7], [8, 9]])\n",
    "\n",
    "print(x.dot(y))\n",
    "print(\"============================\")\n",
    "print(np.dot(x, y))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 34,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([ 6., 15.])"
      ]
     },
     "execution_count": 34,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# 满足 矩阵乘法 规则,计算结果为一维数组，行 * 列 = 数值\n",
    "np.dot(x, np.ones(3))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 35,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([[ 31.60002394, -10.46674904,   1.29606939, -12.67301711,\n",
       "        -10.12098903],\n",
       "       [-10.46674904,   3.62865665,  -0.38832104,   4.17370279,\n",
       "          3.44304935],\n",
       "       [  1.29606939,  -0.38832104,   0.2785569 ,  -0.64094988,\n",
       "         -0.28612645],\n",
       "       [-12.67301711,   4.17370279,  -0.64094988,   5.37281777,\n",
       "          3.88474977],\n",
       "       [-10.12098903,   3.44304935,  -0.28612645,   3.88474977,\n",
       "          3.51577674]])"
      ]
     },
     "execution_count": 35,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# numpy.linalg 中有一组矩阵分解运算，求逆，行列式等等\n",
    "# 使用的是行业标准性线代数库 ，如同 MATLAB、R语言一样\n",
    "\n",
    "# 函数    说明\n",
    "# diag    以一维数组形式返回方阵的对角线元素\n",
    "# dot     矩阵乘法\n",
    "# trace   计算对角线元素的和 —— 矩阵的迹\n",
    "# det     计算矩阵的行列式\n",
    "# eig     计算方阵的特征值和特征向量\n",
    "# inv     计算方阵的逆\n",
    "# pinv    计算矩阵的 Moore-Penrose 伪逆\n",
    "# qr      计算 QP 分解\n",
    "# svd     计算奇异值分解\n",
    "# solve   解线性方程组 Ax = b ，其中 A 为方阵\n",
    "# lstsq   计算 Ax = b 的最小二乘法\n",
    "\n",
    "from numpy.linalg import inv, qr\n",
    "\n",
    "X = np.random.randn(5, 5)\n",
    "mat = X.T.dot(X)\n",
    "inv(mat)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 36,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([[ 1.00000000e+00, -3.55271368e-15, -4.44089210e-16,\n",
       "         1.77635684e-15, -3.55271368e-15],\n",
       "       [-7.10542736e-15,  1.00000000e+00, -4.44089210e-16,\n",
       "         1.24344979e-14,  8.88178420e-15],\n",
       "       [-3.55271368e-15,  1.77635684e-15,  1.00000000e+00,\n",
       "         0.00000000e+00,  8.88178420e-16],\n",
       "       [ 0.00000000e+00,  1.77635684e-15, -1.11022302e-15,\n",
       "         1.00000000e+00, -3.55271368e-15],\n",
       "       [ 1.42108547e-14, -7.10542736e-15, -8.88178420e-16,\n",
       "        -3.55271368e-15,  1.00000000e+00]])"
      ]
     },
     "execution_count": 36,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "mat.dot(inv(mat))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 37,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([[ 0.03298924,  0.12324894, -0.52147611, -0.75306781],\n",
       "       [ 0.7436339 , -2.7712555 ,  0.52393724, -2.12611221],\n",
       "       [ 1.62137807,  0.18671416, -0.24914766, -1.53750184],\n",
       "       [ 0.24322356,  0.62486248, -0.33113795,  0.80587551]])"
      ]
     },
     "execution_count": 37,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# numpy.random 模块对 Python 内置的random进行补充\n",
    "# 增加了用于高效生成多种概率分布的样本值的函数\n",
    "# 用 normal 达到一个标准正态分布的 4X4 样本数组\n",
    "samples = np.random.normal(size=(4,4))\n",
    "samples"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 39,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "3.04 s ± 30.7 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)\n",
      "===========================================================\n",
      "75.7 ms ± 1.09 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)\n"
     ]
    }
   ],
   "source": [
    "# 效率高\n",
    "from random import normalvariate\n",
    "\n",
    "N = 1000000\n",
    "\n",
    "%timeit samples = [normalvariate(0, 1) for _ in range(N)]\n",
    "\n",
    "print(\"===========================================================\")\n",
    "\n",
    "%timeit np.random.normal(size=N)\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 40,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 这种都是伪随机数，通过算法基于随机数生成器种子，在确定性条件下生成的\n",
    "# 更改随机数生成的种子\n",
    "# np.random.seed(1234)\n",
    "\n",
    "# np.random.seed() 创建使用了全局随机种子\n",
    "# 为了避免全局状态，使用 numpy.random.RandomState 创建与其他隔离的随机数生成器\n",
    "\n",
    "# 函数          说明\n",
    "# seed          确定随机数生成器的种子\n",
    "# permutation   返回序列的随机排列或者返回随机排序的范围\n",
    "# shuffle       对一个序列就地随机排序\n",
    "# rand          产生均匀分布的样本值\n",
    "# randint       从给定的上下范围内随机选取整数\n",
    "# randn         产生正态分布的样本值（平局值为 0，标准差为 1）\n",
    "# binomial      产生二项分布的样本值\n",
    "# normal        产生标准正态分布（高斯分布）样本值\n",
    "# beta          产生Beta分布的样本值\n",
    "# chisquare     产生卡方分布的样本值\n",
    "# gamma         产生Gamma分布的样本值\n",
    "# uniform       产生在【0,1】中均匀分布的样本值"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 42,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[<matplotlib.lines.Line2D at 0xc50fb48>]"
      ]
     },
     "execution_count": 42,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "image/png": "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\n",
      "text/plain": [
       "<Figure size 432x288 with 1 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "# 随机漫步\n",
    "\n",
    "# 模拟随机漫步说明如何运用数组运算\n",
    "\n",
    "# 简单随机漫步\n",
    "# 从 0 开始， 步长 1 和 -1 出现的概率相等\n",
    "\n",
    "# 内置的random模块以纯Python方式实现1000步的随机漫步\n",
    "import random\n",
    "position = 0\n",
    "walk = [position]\n",
    "steps = 1000\n",
    "for i in range(steps):\n",
    "    step = i if random.randint(0,1) else -1\n",
    "    position += step\n",
    "    walk.append(position)\n",
    "    \n",
    "# 根据前 100 个随机漫步输出折线图\n",
    "plt.plot(walk[:100])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 43,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "-50"
      ]
     },
     "execution_count": 43,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# 就是随机漫步中每一步的累计和，可用一个数组运算来实现\n",
    "# 用 np.random 模块一次性产生1000个 “抛硬币” 结果，分别设置 1 -1\n",
    "nsteps = 1000\n",
    "draws = np.random.randint(0, 2, size=nsteps)\n",
    "steps = np.where(draws > 0, 1, -1)\n",
    "walk = steps.cumsum()\n",
    "walk.min()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 44,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "3"
      ]
     },
     "execution_count": 44,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "walk.max()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 45,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "69"
      ]
     },
     "execution_count": 45,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# 复杂点的统计任务——首次穿越时间， 即随机漫步过程中第一次到达某个特定值的时间\n",
    "# 假设想要知道本次随机漫步需要多久才能距离初始 0 点至少 10 步远（任一方向均可）\n",
    "# np.abs(walk)>=10 可以得到一个布尔型数组， 它表示的是距离是否达到或超过10，\n",
    "# 而想要知道的是第一个10或－10的索引。 \n",
    "# 可以用argmax来解决这个问题， 它返回的是该布尔型数组第一个最大值的索引（True就是最大值） ：\n",
    "(np.abs(walk) >= 10).argmax()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 47,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([[  1,   0,   1, ...,  48,  49,  50],\n",
       "       [ -1,  -2,  -1, ..., -16, -17, -18],\n",
       "       [  1,   2,   1, ...,  12,  13,  12],\n",
       "       ...,\n",
       "       [ -1,   0,   1, ...,  10,   9,   8],\n",
       "       [  1,   2,   3, ...,  34,  35,  36],\n",
       "       [ -1,   0,  -1, ...,  24,  25,  24]], dtype=int32)"
      ]
     },
     "execution_count": 47,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# 一次模拟多个随机漫步\n",
    "nwalks = 5000\n",
    "nsteps = 1000\n",
    "draws = np.random.randint(0, 2, size=(nwalks, nsteps))\n",
    "steps = np.where(draws > 0, 1, -1)\n",
    "walks = steps.cumsum(1)\n",
    "walks"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 48,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "130"
      ]
     },
     "execution_count": 48,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "walks.max()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 49,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "-116"
      ]
     },
     "execution_count": 49,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "walks.min()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 50,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([ True, False, False, ...,  True,  True,  True])"
      ]
     },
     "execution_count": 50,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# 计算 30 或者 -30 的最小穿越时间\n",
    "# 不是 5000 个过程都达到 30 需要 any 方法进行检测\n",
    "hits30 = (np.abs(walks) >= 30).any(1)\n",
    "hits30"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 51,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "3364"
      ]
     },
     "execution_count": 51,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "hits30.sum()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 53,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "510.35850178359095"
      ]
     },
     "execution_count": 53,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# 利用布尔型数组选出穿越 30 的随机漫步，调用 argmax 在轴1上获取穿越时间\n",
    "crossing_times = (np.abs(walks[hits30]) >= 30).argmax(1)\n",
    "crossing_times.mean()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.7.6"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}
